#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

#define PTRSIZE 128
#define BUFSIZE 256

// GPIO硬件可变信息,每一组GPIO对应一个此结构
struct gpio_desc {
	int group;
	int num[16];
};
// E2000D 6组GPIO
static struct gpio_desc gpio_val[6];

// E2000D所有的GPIO编号都存储到gpio_val中
void gpio_num_init(void);
// group组的第n号引脚的编号
int gpio_get(int group, int n);
// 将value写入配置文件pptr/attr
int gpio_write(char *pptr, char *attr, const char value);

int open_gpio_attr(int num, const char *attr, mode_t mode);

int set_gpio_attr(int num, const char *attr, const char *value);

int check_gpio_dir(int num);

int create_gpio_dir(int num);

long long diff_timespec(const struct timespec *time1, const struct timespec *time0)
{
	long long diff;

	diff = (time1->tv_sec - time0->tv_sec) * 1000000000LL;
	diff += (time1->tv_nsec - time0->tv_nsec);

	printf("time1: %ld %ld\n", time1->tv_sec, time1->tv_nsec);
	printf("time0: %ld %ld\n", time0->tv_sec, time0->tv_nsec);

	printf("diff: %lld\n", diff);

	return diff;
}

int main(int argc, char *argv[])
{
	char pptr[PTRSIZE] = {};
	char buf[BUFSIZE] = {};
	int pin_key1;
	int pin_leds[3];
	int pin_send, pin_recv;
	int fd;
	int res = 0;
	int start_recv = 0;
	int i;

	struct timespec ts_send, ts_recv, ts_recv2;
	long long ts_diff;
	float dist;

	struct pollfd pfd;

	// 获取对应gpio的编号
	gpio_num_init();

	pin_send = gpio_get(4, 4);
	pin_recv = gpio_get(4, 5);
	pin_key1 = gpio_get(4, 1);
	pin_leds[0] = gpio_get(0, 13);
	pin_leds[1] = gpio_get(1, 1);
	pin_leds[2] = gpio_get(5, 0);

	if (check_gpio_dir(pin_send)) {
		res = create_gpio_dir(pin_send);
	}
	if (res != 0) {
		exit(1);
	}
	if (check_gpio_dir(pin_recv)) {
		res = create_gpio_dir(pin_recv);
	}
	if (res != 0) {
		exit(1);
	}
	
	set_gpio_attr(pin_recv, "edge", "both");
	set_gpio_attr(pin_recv, "direction", "in");
	set_gpio_attr(pin_send, "direction", "out");

	for (i = 0; i < 3; i++) {
		if (check_gpio_dir(pin_leds[i])) {
			res = create_gpio_dir(pin_leds[i]);
		}
		if (res != 0) {
			exit(1);
		}

		set_gpio_attr(pin_leds[i], "direction", "out");
		set_gpio_attr(pin_leds[i], "value", "0");
	}

	while (1) {
		start_recv = 0;

		set_gpio_attr(pin_send, "value", "1");
		usleep(10);
		set_gpio_attr(pin_send, "value", "0");

		fd = open_gpio_attr(pin_recv, "value", O_RDONLY);
		if (-1 == fd) {
			exit(1);
		}
		pfd.fd = fd;
		pfd.events = POLLPRI; // 中断

		memset(buf, 0, BUFSIZE);
		while (1) {
			if (-1 == poll(&pfd, 1, -1)) {
				perror("poll()");
				close(fd);
				exit(1);
			}

			if (pfd.revents & POLLPRI) {
				lseek(fd, 0, SEEK_SET);
				memset(buf, 0, BUFSIZE);
				if (-1 == read(fd, buf, BUFSIZE)) {
					perror("read()");
					close(fd);
					exit(1);
				}
				printf("UltraSonic interrupt event\n");
				printf("UltraSonic value is: %s", buf);

				if (buf[0] == '1') {
					clock_gettime(CLOCK_REALTIME, &ts_recv);
					start_recv = 1;
				} else if (start_recv) {
					clock_gettime(CLOCK_REALTIME, &ts_recv2);
					ts_diff = diff_timespec(&ts_recv2, &ts_recv);
					dist = ts_diff / 1000 / 29.4;
					printf("Distance: %f\n", dist);

					for (i = 0; i < 3; i++) {
						set_gpio_attr(pin_leds[i], "value", dist < 10 ? "1" : "0");
						set_gpio_attr(pin_leds[i], "value", dist < 10 ? "1" : "0");
						set_gpio_attr(pin_leds[i], "value", dist < 10 ? "1" : "0");
					}

					break;
				}
			}
		}

		usleep(500000); // Wait 0.5s
	}

	return 0;
}

void gpio_num_init(void)
{
	int i, j;

	// 组
	for (i = 0; i < 6; i++) {
		gpio_val[i].group = i;
		// 引脚
		for (j = 0; j < 16; j++) {
			// 16 * i每一组引脚编号起始值496 - 16 * i
			gpio_val[i].num[j] = 496 - 16 * i + j;
		}
	}
}

int gpio_get(int group, int n)
{
	return gpio_val[group].num[n];
}

int open_gpio_attr(int num, const char *attr, mode_t mode)
{
	char path[PTRSIZE] = {};

	snprintf(path, PTRSIZE, "/sys/class/gpio/gpio%d/%s", num, attr);
	return open(path, mode);
}

int set_gpio_attr(int num, const char *attr, const char *value)
{
	int fd = open_gpio_attr(num, attr, O_WRONLY);
	if (-1 == fd) {
		perror("Error opening GPIO attribute file");
		return -1;
	}
	printf("Write \"%s\" into [%d]/%s\n", value, num, attr);

	write(fd, value, strlen(value));
	close(fd);

	return 0;
}

int check_gpio_dir(int num)
{
	char path[PTRSIZE] = {};
	
	snprintf(path, PTRSIZE, "/sys/class/gpio/gpio%d", num);
	return access(path, F_OK);
}

int create_gpio_dir(int num)
{
	char gpio_num[10] = {};
	int export_fd = open("/sys/class/gpio/export", O_WRONLY);

	if (-1 == export_fd) {
		perror("Error opening GPIO export file");
		return -1;
	}
	printf("Creating GPIO dir %d\n", num);

	snprintf(gpio_num, 10, "%d", num);
	write(export_fd, gpio_num, strlen(gpio_num));

	close(export_fd);

	return 0;
}

